﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SeizyUtils.AppCode.Common.Utilities
{

    /// <summary>
    /// string,intなどのキーを元にIBeanを返す構成のディクショナリクラスです。
    /// このディクショナリは、複数の値をキーに持つIBeanを管理するために利用します。
    /// 以下のようなツリーを構成しています。
    ///  KeyA
    ///  - KeyB
    ///    - KeyC
    ///      - KeyD
    ///        - IBean
    /// 複数のキー値を持つDictionaryのイメージで使えます。
    /// この機能は例えば、複数の主キーを持つレコードのマージ処理を高速にこなすなどに利用できます。
    /// </summary>
    class HierarchyMap<T>
    {
        /// <summary>
        /// 内部に階層上にデータを持ちます。
        /// </summary>
        private Node<T> _rootNode = new Node<T>();

        /// <summary>
        /// 結果情報を一列で管理するリストです。
        /// </summary>
        private IList<T> _result = new List<T>();


        /// <summary>
        /// キーに紐づくデータを取得します。
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        public T find(params Object[] keys)
        {
            Node<T> node = findNode(keys);
            return node.hasContent() ? node.getContent() : default(T);
        }

        /// <summary>
        /// 指定したキー階層にデータを配置します。
        /// </summary>
        /// <param name="bean"></param>
        /// <param name="keys"></param>
        public void put(T bean, params Object[] keys)
        {
            Node<T> node = findNode(keys);
            node.setContent(bean);
            _result.Add(bean);
        }

        /// <summary>
        /// 指定した階層のデータを削除します。
        /// </summary>
        /// <param name="keys"></param>
        public void remove(params Object[] keys)
        {
            Node<T> node = findNode(keys);
            if (node.getContent() == null)
            {
                T content = node.getContent();
                _result.Remove(content);
                node.clearContent();
            }
        }

        /// <summary>
        /// 全レコードを一列のIListとして返します。
        /// </summary>
        /// <returns></returns>
        public IList<T> toList()
        {
            return _result;
        }

        /// <summary>
        /// データの件数を返します。
        /// </summary>
        /// <returns></returns>
        public int getResultCount()
        {
            return _result.Count;
        }

        /// <summary>
        /// ノードを検索します。
        /// ノードが存在しない場合はキーに対応するノードを生成します。
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        private Node<T> findNode(params Object[] keys)
        {
            Node<T> current = _rootNode;
            foreach (Object key in keys)
            {
                Node<T> node = current.find(key);
                if (node == null)
                {
                    throw new ApplicationException("ここに来ることはありません。");
                }
                else
                {
                    current = node;
                }
            }
            return current;
        }
    }

    /// <summary>
    /// ノードを表すクラスです。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class Node<T>
    {
        private Object _key;

        private Node<T> _parent;

        private T _content;

        private Dictionary<Object, Node<T>> _children = new Dictionary<object, Node<T>>();

        public Node()
        {
            this._key = "";

        }
        private Node(Object key, Node<T> parent)
        {
            this._key = key;
            this._parent = parent;
        }

        public void setContent(T value)
        {
            _content = value;
        }

        public Node<T> find(Object key)
        {
            Node<T> node = null;
            if (!_children.ContainsKey(key))
            {
                node = new Node<T>(key, this);

                _children.Add(key, node);
            }
            else
            {
                node = _children[key];
            }
            return node;
        }

        public bool hasContent()
        {
            return _content != null;
        }

        public T getContent()
        {
            return _content;
        }

        public void clearContent()
        {
            _content = default(T);
        }

        public override string ToString()
        {
            return _key.ToString();
        }

    }
}
